﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Windows.Forms;
using System.Drawing;
using ZedGraph;
using System.ComponentModel;

namespace Atentis_Connector
{
	/// <summary>
	/// Графическое представление свечей.
	/// </summary>
	class StockViewer
	{
		private int m_DrawLast;
		/// <summary>
		/// Сколько последних значений отображать.
		/// </summary>
		public int DrawLast { get { return m_DrawLast; } set { m_DrawLast = value; } }

		private Form m_MainForm;
		/// <summary>
		/// Событие - форма с графиком закрыта
		/// </summary>
		public event EventHandler CloseForm;
		void OnCloseForm(EventArgs e)
		{
			EventHandler closeForm = CloseForm;
			if (closeForm != null)
				closeForm(this, e);
		}

		private List<string> m_AllSeries;
		private ZedGraphControl m_ZedGraph;
		private List<Candle> m_Candles;
		private IndQueryForm m_IndQueryForm;

		private List<Dictionary<int, decimal>> m_Indicators;
		public List<Dictionary<int, decimal>> Indicators
		{
			get { return m_Indicators; }
			set { m_Indicators = value; }
		}

		public StockViewer()
		{
			m_MainForm = new Form();
			m_MainForm.Left = 100;
			m_MainForm.Top = 100;
			m_MainForm.Width = 500;
			m_MainForm.Height = 500;
			m_MainForm.FormClosing += m_MainForm_FormClosing;
			InitChart(m_MainForm);

			m_MainForm.Show();

			m_AllSeries = new List<string>();
			Indicators = new List<Dictionary<int, decimal>>();

		}

		void m_MainForm_FormClosing(object sender, FormClosingEventArgs e)
		{
			OnCloseForm(new EventArgs());
		}

		void InitChart(Form form)
		{
			this.m_ZedGraph = new ZedGraph.ZedGraphControl();
			// !!! Подпишемся на событие, которое будет возникать перед тем, 
			// как будет показано контекстное меню.
			//m_ZedGraph.ContextMenuBuilder += m_ZedGraph_ContextMenuBuilder;
			m_ZedGraph.CursorChanged += new EventHandler(zedGraph_CursorChanged);

			form.SuspendLayout();
			// 
			// m_ZedGraph
			// 
			this.m_ZedGraph.Dock = System.Windows.Forms.DockStyle.Fill;
			this.m_ZedGraph.Location = new System.Drawing.Point(0, 0);
			this.m_ZedGraph.Name = "m_ZedGraph";
			this.m_ZedGraph.ScrollGrace = 0;
			this.m_ZedGraph.ScrollMaxX = 0;
			this.m_ZedGraph.ScrollMaxY = 0;
			this.m_ZedGraph.ScrollMaxY2 = 0;
			this.m_ZedGraph.ScrollMinX = 0;
			this.m_ZedGraph.ScrollMinY = 0;
			this.m_ZedGraph.ScrollMinY2 = 0;
			this.m_ZedGraph.Size = new System.Drawing.Size(592, 471);
			this.m_ZedGraph.TabIndex = 0;
			this.m_ZedGraph.Dock = System.Windows.Forms.DockStyle.Fill;

			//--------------------------------------------------------------------------------
			// Создаем экземпляр класса MasterPane, который представляет собой область, 
			// на которйо "лежат" все графики (экземпляры класса GraphPane)
			ZedGraph.MasterPane masterPane = this.m_ZedGraph.MasterPane;

			// По умолчанию в MasterPane содержится один экземпляр класса GraphPane 
			// (который можно получить из свойства zedGraph.GraphPane)
			// Очистим этот список, так как потом мы будем создавать графики вручную
			masterPane.PaneList.Clear();

			// Добавим три графика
			// Создаем экземпляр класса GraphPane, представляющий собой один график
			GraphPane candlePane = new GraphPane();
			InitPane(candlePane, "Время", "Цена", "Свечи", false);
			masterPane.Add(candlePane);

			// Создаем экземпляр класса GraphPane, представляющий собой один график
			GraphPane awesomeOscillatorPane = new GraphPane();
			// Добавим новый график в MasterPane
			masterPane.Add(awesomeOscillatorPane);
			InitPane(awesomeOscillatorPane, "Время", "Значение", "Vol", false);

			// Будем размещать добавленные графики в MasterPane
			using (Graphics g = m_MainForm.CreateGraphics())
			{
				// Закомментарены разные варианты (не все) размещения графиков.

				// Графики будут размещены в один столбец друг под другом
				masterPane.SetLayout(g, PaneLayout.SingleColumn);
				masterPane.SetLayout(g, true, new int[] { 1, 1 }, new float[] { 0.60f, 0.40f });

				//Графики будут размещены в одну строку друг за другом
				//masterPane.SetLayout (g, PaneLayout.SingleRow);

				// Графики будут размещены в две строки, 
				// в первой будет один столбец, а во второй - две
				//masterPane.SetLayout(g, PaneLayout.ExplicitCol12);
			}


			m_Candles = new List<Candle>();

			form.Controls.Add(this.m_ZedGraph);
			form.Controls.SetChildIndex(this.m_ZedGraph, 0);
			form.ResumeLayout(false);

		}

			/// <summary>
			/// Событие при изменении курсора
			/// </summary>
			/// <param name="sender"></param>
			/// <param name="e"></param>
			void zedGraph_CursorChanged (object sender, EventArgs e)
			{
				// Разрешим ZedGraph'у изменять курсор при выделении участка на графике, 
				// а также при перемещении графика.
				// В обоих случаях курсор будет "захвачен"
				// Если курсор не "захвачен", то установим курсор обратно в виде стрелки.
				// Если нужно запретить изменять курсор в любом случае, 
				// то достаточно просто убрать условие. 

				if (!m_ZedGraph.Capture)
				{
					m_ZedGraph.Cursor = Cursors.Arrow;
				}
			}

		/// <summary>
		/// Обработчик события, который вызывается, перед показом контекстного меню
		/// </summary>
		/// <param name="sender">Компонент ZedGraph</param>
		/// <param name="menuStrip">Контекстное меню, которое будет показано</param>
		/// <param name="mousePt">Координаты курсора мыши</param>
		/// <param name="objState">Состояние контекстного меню. Описывает объект, на который кликнули.</param>

		void m_ZedGraph_ContextMenuBuilder(ZedGraphControl sender, ContextMenuStrip menuStrip, Point mousePt, ZedGraphControl.ContextMenuObjectState objState)
		{
			// Добавим свой пункт меню
			ToolStripItem newMenuItem = new ToolStripMenuItem ("Добавить индикатор");
			menuStrip.Items.Add (newMenuItem);
			//используем обработчик EventHandler
			newMenuItem.Click += new EventHandler(newMenuItem_Click);
		}

		// и обработаем соответственно событие
		private void newMenuItem_Click(object sender, EventArgs e)
		{
			m_IndQueryForm = new IndQueryForm();
			m_IndQueryForm.IndSelected += m_IndQueryForm_IndSelected;
			m_IndQueryForm.Show();
		}

		void m_IndQueryForm_IndSelected(object sender, EventArgs e)
		{
			if (e != null && e is AtentisConnectorEventArgs)
			{
				var indName = (e as AtentisConnectorEventArgs).IndName;
				/* Нужен код для добавления индикатора на график. */
				if (indName == "SMA")
				{
					//SimpleMovingAverage indicator = new SimpleMovingAverage();
					//m_Indicators.Add(indicator);
				}
			}
			m_IndQueryForm = null;
		}

		public void AddSeries(string seriesName)
		{
			var ser = m_AllSeries.Find(t => t == seriesName);
			if (ser != null)
			{
				Console.WriteLine("График с таким именем уже существует!");
				return;
			}

			this.m_AllSeries.Add(seriesName);

		}

		private delegate void UICallerDelegate(double value, string seriesName);

		public bool HasSeriesName(string seriesName)
		{
			if (m_AllSeries == null)
				return false;
			return (m_AllSeries.Find(t => t == seriesName) != null);
		}

		/// <summary>
		/// Нарисовать свечи и индикаторы
		/// </summary>
		/// <param name="inCandles">Список свечей</param>
		public void DrawCandlesAndIndicators(List<Candle> inCandles)
		{

				List<Candle> candles = new List<Candle>();
				for (int i = 0; i < inCandles.Count; i++)
				{
					candles.Add(inCandles[i]);
				}
				if (candles == null || candles.Count == 0)
					return;
				if (m_ZedGraph == null || m_ZedGraph.MasterPane == null) return;
				var candlePane = m_ZedGraph.MasterPane.PaneList[0];
				//Выведем свечи
				DrawCandlesGraph(candlePane, candles);
                // Добавим новый график в MasterPane
                var volPane = m_ZedGraph.MasterPane.PaneList[1];
                //Выведем AO
                DrawVolGraph(volPane);
				this.m_ZedGraph.AxisChange();
				this.m_ZedGraph.Invalidate();

		}

		private void InitPane(GraphPane candlePane, string XAxisLabel, string YAxisLabel, string GraphLabel, bool ShowGrid)
		{
			var pane = candlePane;
			pane.XAxis.Title = new AxisLabel(XAxisLabel, "Verdana", 10, Color.Black, true, false, false);
			pane.YAxis.Title = new AxisLabel(YAxisLabel, "Verdana", 10, Color.Black, true, false, false);
			pane.Title.Text = GraphLabel;

			pane.Chart.Fill = new Fill(Color.Gray, Color.Gray, 45.0f);
			pane.Fill = new Fill(Color.Gray, Color.Gray, 45.0f);

			//			pane.Chart.Fill = new Fill(Color.White, Color.LightGoldenrodYellow, 45.0f);
			//			pane.Fill = new Fill(Color.White, Color.FromArgb(220, 220, 255), 45.0f);
			// !!! Изменим угол наклона меток по осям. 
			// Углы задаются в градусах
			pane.XAxis.Scale.FontSpec.Angle = 90;
			if (ShowGrid)
			{
				#region Включение СЕТКИ

				// Включаем отображение сетки напротив крупных рисок по оси X
				pane.XAxis.MajorGrid.IsVisible = true;
				// Задаем вид пунктирной линии для крупных рисок по оси X:
				// Длина штрихов равна 10 пикселям, ... 
				pane.XAxis.MajorGrid.DashOn = 20;
				// затем 5 пикселей - пропуск
				pane.XAxis.MajorGrid.DashOff = 10;

				// Включаем отображение сетки напротив крупных рисок по оси Y
				pane.YAxis.MajorGrid.IsVisible = true;
				// Аналогично задаем вид пунктирной линии для крупных рисок по оси Y
				pane.YAxis.MajorGrid.DashOn = 20;
				pane.YAxis.MajorGrid.DashOff = 10;


				// Включаем отображение сетки напротив мелких рисок по оси X
				pane.YAxis.MinorGrid.IsVisible = true;
				// Задаем вид пунктирной линии для крупных рисок по оси Y: 
				// Длина штрихов равна одному пикселю, ... 
				pane.YAxis.MinorGrid.DashOn = 2;
				// затем 2 пикселя - пропуск
				pane.YAxis.MinorGrid.DashOff = 5;

				// Включаем отображение сетки напротив мелких рисок по оси Y
				pane.XAxis.MinorGrid.IsVisible = true;
				// Аналогично задаем вид пунктирной линии для крупных рисок по оси Y
				pane.XAxis.MinorGrid.DashOn = 2;
				pane.XAxis.MinorGrid.DashOff = 5;

				#endregion Включение СЕТКИ
			}
		}
        //--**

        private void DrawCandlesGraph(GraphPane candlePane, List<Candle> candles)
        {

            GraphPane myPane = candlePane;
            if (myPane == null) return;

            myPane.CurveList.Clear();
            myPane.GraphObjList.Clear();
            m_Candles.Clear();

            foreach (var candle in candles)
            {
                m_Candles.Add(candle);
            }

            string[] names = new string[DrawLast];

            StockPointList spl = new StockPointList();
            StockPt pt;

            int candles_Count = m_Candles.Count;
            if (candles_Count > DrawLast)
            {
                int j = 0;
                for (int i = candles_Count - DrawLast; i < candles_Count; i++)
                {
                    var cdl = m_Candles[i];
                    pt = new StockPt(DateTime.Now.AddDays(1).ToOADate(), cdl.High, cdl.Low, cdl.Open, cdl.Close, cdl.Volume);
                    names[j] = string.Format("[{0:HH:mm:ss}]", cdl.Date);
                    j++;
                    spl.Add(pt);
                }
            }
            else
            {
                for (int i = 0; i < candles_Count; i++)
                {
                    var cdl = m_Candles[i];
                    pt = new StockPt(DateTime.Now.AddDays(1).ToOADate(), cdl.High, cdl.Low, cdl.Open, cdl.Close, cdl.Volume);
                    names[i] = string.Format("[{0:HH:mm:ss}]", cdl.Date);
                    spl.Add(pt);
                }
            }
            JapaneseCandleStickItem myCurve = myPane.AddJapaneseCandleStick(candles[0].Ticker, spl);

            myCurve.Stick.IsAutoSize = true;
            myCurve.Stick.Color = Color.Black;

            myCurve.Stick.FallingBorder.Color = Color.Black;
            myCurve.Stick.FallingFill.Color = Color.Black;

            myCurve.Stick.RisingBorder.Color = Color.Black;
            myCurve.Stick.RisingFill.Color = Color.White;

            // Настроим ось X так, чтобы она отображала текстовые данные
            myPane.XAxis.Type = AxisType.Text;

            // Уставим для оси наши подписи
            if (names.Count() <= DrawLast)
                myPane.XAxis.Scale.TextLabels = names;
            else
            {
                string[] cropedNames = new string[DrawLast];

                int k = 0;

                for (int j = 0; j < DrawLast - 1; j++)
                {
                    cropedNames[k] = names[j];
                    k++;
                }
                myPane.XAxis.Scale.TextLabels = cropedNames;
            }
        }

        private void DrawVolGraph(GraphPane pane)
        {
            pane.CurveList.Clear();
            pane.GraphObjList.Clear();

            int itemscount = Indicators[1].Count();

            if (itemscount > DrawLast)
            {
                // Подписи под столбиками
                string[] names = new string[DrawLast + 1];
                // Высота столбиков
                double[] values = new double[DrawLast + 1];

                int i = 0;
                int j = 0;
                foreach (var bar in Indicators[1])
                {
                    if (i > itemscount - DrawLast)
                    {
                        names[j] = string.Format("{0}", i);
                        values[j] = (double)bar.Value;
                        j++;
                    }
                    i++;
                }
                /*
                // Заполним данные
                int j = 0;
                for (int i = itemscount - DrawLast; i < itemscount; i++)
                {
                    names[j] = string.Format("{0}", i);
                    values[j] = (double)Indicators[1].Values.ElementAt(i);
                    j++;
                }
                */
                BarItem curve = pane.AddBar(string.Empty, null, values, Color.Blue);

                // Настроим ось X так, чтобы она отображала текстовые данные
                pane.XAxis.Type = AxisType.Text;

                // Уставим для оси наши подписи
                pane.XAxis.Scale.TextLabels = names;
            }
            else
            {
                // Подписи под столбиками
                itemscount++;
                string[] names = new string[itemscount];
                // Высота столбиков
                double[] values = new double[itemscount];

                int i = 0;
                foreach (var bar in Indicators[1])
                {
                    names[i] = string.Format("{0}", i);
                    values[i] = (double)bar.Value;
                    i++;
                }
                /*
                // Заполним данные
                for (int i = 0; i < itemscount; i++)
                {
                    names[i] = string.Format("{0}", i);
                    values[i] = (double)Indicators[1].Values.ElementAt(i);
                }
                */
                BarItem curve = pane.AddBar(string.Empty, null, values, Color.Blue);

                // Настроим ось X так, чтобы она отображала текстовые данные
                pane.XAxis.Type = AxisType.Text;

                // Уставим для оси наши подписи
                pane.XAxis.Scale.TextLabels = names;
            }
            // Создадим кривую-гистограмму
            // Первый параметр - название кривой для легенды
            // Второй параметр - значения для оси X, т.к. у нас по этой оси будет идти текст, а функция ожидает тип параметра double[], то пока передаем null
            // Третий параметр - значения для оси Y
            // Четвертый параметр - цвет

        }

		/// <summary>
		/// Закрыть форму
		/// </summary>
		public void CloseThatForm()
		{
			this.m_MainForm.Close();
		}

	}
}
